package sybrevolutions


import forvalues._

object forvalues_caseclasses_onclassfile {
  
  def ctorlist[T](clazz:Class[T])  = clazz.getDeclaredConstructors().toList
  def caseProductView[T](clazz:Class[T]): T=>Option[Product] = { (t:T) =>
    if (clazz.getInterfaces.contains(classOf[Product])) Some(t.asInstanceOf[Product]) else None
  }
  
  

/*
 * These converters try to convert a clazz of certain properties into the proper TypeMan
 * Eventually instead of deriving a typeman from a manifest externally, the manifest implementation itself should do this.
 */

def CaseClass1ToValSpineWithEnv[T<:Product,Param1](env:Object)(clazz:Class[T])(implicit p1tyman: TypeMan[Param1]) :TypeMan[T] = {
  assume (clazz != null, "CaseClass1BisToValSpine received null manifest")
  assume (p1tyman != null, "CaseClass1BisToValSpine received null typeman for param 1")
  
  
  implicit def ToProduct1(t:T):Product1[Param1] = {
    val maybeproduct = caseProductView(clazz)(t)
     assume (maybeproduct.isDefined, "CaseClass1oValSpine received a non-product class")
    val asproduct = maybeproduct.get
    assume (asproduct.productArity == 1, "CaseClass1ToValSpine received a product of wrong arity")
    //case classes do not implement a specific productn, just product in general!
    //val asproduct1: Product1[Param1] = asproduct.asInstanceOf[Product1[Param1]] //Param1 lost in erasure here
    val param1ok: Param1 = asproduct.productElement(0).asInstanceOf[Param1] //check whether type ok, unfortunately type nowhere mentioned
    new Tuple1[Param1](asproduct.productElement(0).asInstanceOf[Param1])
  } 
  

  val constr = clazz.getDeclaredConstructors()(0)
  new TypeMan[T]{
    def toValSpine(t:T) = { val constrf = (p1:Param1)=>constr.newInstance(
                                {val arr=new Array[Object](2);arr(0)=env;arr(1)=p1.asInstanceOf[Object];arr}).asInstanceOf[T]//blow up for unboxed types?
                            val p1wrapped = Typed( t ._1, p1tyman)
                            Apped(ConstrExt(constrf, new {val n = clazz.getName()})
                                 , p1wrapped)
                           }
    val classname = clazz.getName
    override def toString():String = classname
    }
}

def CaseClass2ToValSpineWithEnv[T<:Product,Param1,Param2](env:Object)(clazz:Class[T])(implicit p1tyman: TypeMan[Param1], p2tyman:TypeMan[Param2]) :TypeMan[T] =  {
  assume (clazz != null, "CaseClass2ToValSpine received null manifest")
  assume (p1tyman != null, "CaseClass2ToValSpine received null typeman for param 1")
  assume (p2tyman != null, "CaseClass2ToValSpine received null typeman for param 2")
  
  implicit def ToProduct2(t:T):Product2[Param1,Param2] = {
    val maybeproduct = caseProductView(clazz)(t)
     assume (maybeproduct.isDefined, "CaseClass12ToValSpine received a non-product class")
    val asproduct = maybeproduct.get
    assume (asproduct.productArity == 2, "CaseClass2ToValSpine received a product of wrong arity")
    //case classes do not implement a specific producn, just product in general!
    //val asproduct1: Product1[Param1] = asproduct.asInstanceOf[Product1[Param1]] //Param1 lost in erasure here
    val param1ok: Param1 = asproduct.productElement(0).asInstanceOf[Param1] //check whether type ok, unfortunately type nowhere mentioned
    val param2ok: Param2 = asproduct.productElement(1).asInstanceOf[Param2] //check whether type ok, unfortunately type nowhere mentioned
    new Tuple2[Param1,Param2](param1ok,param2ok)
  } 
  

  assume(clazz.getDeclaredConstructors().size==1, "nb of constructors for "+clazz+ " = "+clazz.getDeclaredConstructors().size);
  val constr = clazz.getDeclaredConstructors()(0)
  new TypeMan[T]{
    def toValSpine(t:T) = { val constrf = (p1:Param1)=>(p2:Param2)=>constr.newInstance(
                                {val arr=new Array[Object](3);arr(0)=env;arr(1)=p1.asInstanceOf[Object];arr(2)=p2.asInstanceOf[Object];arr}).asInstanceOf[T]//blow up for unboxed types?
                            val p1wrapped = Typed(t._1, p1tyman)
                            val p2wrapped = Typed(t._2, p2tyman)
                            Apped(Apped(ConstrExt(constrf, new {val n = clazz.getName()})
                                 , p1wrapped)
                                 , p2wrapped)
                           }
    val classname = clazz.getName
    override def toString():String = classname
    }
}

def CaseClass3ToValSpineWithEnv[T<:Product,Param1,Param2,Param3](env:Object)(clazz:Class[T])(implicit p1tyman: TypeMan[Param1], p2tyman:TypeMan[Param2], p3tyman: TypeMan[Param3]) :TypeMan[T] =  {
  assume (clazz != null, "CaseClass3ToValSpine received null manifest")
  assume (p1tyman != null, "CaseClass3ToValSpine received null typeman for param 1")
  assume (p2tyman != null, "CaseClass3ToValSpine received null typeman for param 2")
  assume (p3tyman != null, "CaseClass3ToValSpine received null typeman for param 3")

  
  implicit def ToProduct3(t:T):Product3[Param1,Param2,Param3] = {
    val maybeproduct = caseProductView(clazz)(t)
     assume (maybeproduct.isDefined, "CaseClass3ToValSpine received a non-product class")
    val asproduct = maybeproduct.get
    assume (asproduct.productArity == 3, "CaseClass3ToValSpine received a product of wrong arity")
    //case classes do not implement a specific producn, just product in general!
    //val asproduct1: Product1[Param1] = asproduct.asInstanceOf[Product1[Param1]] //Param1 lost in erasure here
    val param1ok: Param1 = asproduct.productElement(0).asInstanceOf[Param1] //check whether type ok, unfortunately type nowhere mentioned
    val param2ok: Param2 = asproduct.productElement(1).asInstanceOf[Param2] //check whether type ok, unfortunately type nowhere mentioned
    val param3ok: Param3 = asproduct.productElement(2).asInstanceOf[Param3] //check whether type ok, unfortunately type nowhere mentioned
    new Tuple3[Param1,Param2,Param3](param1ok,param2ok,param3ok)
  } 
  

  
  assume(clazz.getDeclaredConstructors().size==1, "nb of constructors for "+clazz+ " = "+clazz.getDeclaredConstructors().size);
  val constr = clazz.getDeclaredConstructors()(0)
  new TypeMan[T]{
    def toValSpine(t:T) = { val constrf = (p1:Param1)=>(p2:Param2)=>(p3:Param3)=>constr.newInstance(
                                {val arr=new Array[Object](4);arr(0)=env;
                                                              arr(1)=p1.asInstanceOf[Object];
                                                              arr(2)=p2.asInstanceOf[Object];
                                                              arr(3)=p3.asInstanceOf[Object];arr}).asInstanceOf[T]//blow up for unboxed types?
                            val p1wrapped = Typed(t._1, p1tyman)
                            val p2wrapped = Typed(t._2, p2tyman)
                            val p3wrapped = Typed(t._3, p3tyman)
                            Apped(Apped(Apped(ConstrExt(constrf, new {val n = clazz.getName()})
                                 , p1wrapped)
                                 , p2wrapped)
                                 , p3wrapped)
                            }
    val classname = clazz.getName
    override def toString():String = classname
    }
}



}